/**
 * Copyright (C) 2010 Hamburg Sud
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hamburgsud.log4testing.core.output.util;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.regex.Matcher;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.Node;

import com.hamburgsud.log4testing.core.configuration.Log4TestingConfiguration;
import com.hamburgsud.log4testing.core.data.Path;
import com.hamburgsud.log4testing.core.data.TestCase;
import com.hamburgsud.log4testing.core.data.TestStep;
import com.hamburgsud.log4testing.core.data.TestStepContainer;
import com.hamburgsud.log4testing.core.data.TestStepGroup;
import com.hamburgsud.log4testing.core.data.attachment.Attachment;
import com.hamburgsud.log4testing.core.data.attachment.StringAttachment;
import com.hamburgsud.log4testing.core.output.writer.FileWriter;

public class File {

	final private static Logger logger = Logger.getLogger(File.class);

	private static final String XPATH_REPLACE = "/log4testing/attachment//replace";
	private static final String XPATH_REPLACE_REGEX = "regex";
	private static final String XPATH_REPLACE_REPLACEMENT = "replacement";

	private FileWriter<?> fileWriter;

	private File() {
		super();
	}

	public File(final FileWriter<?> fileWriter) {
		this();
		this.fileWriter = fileWriter;
	}

	public java.io.File write(final TestStepContainer testStepContainer,
			String content) {
		if (content == null) {
			content = "";
		}
		java.io.File outputFile = null;
		try {
			outputFile = getOutputFile(testStepContainer);
			write(outputFile, content.getBytes());
		} catch (IOException e) {
			logger.error("The content of the test step container "
					+ testStepContainer.getName()
					+ " couldn't be written due to a occurred exception.", e);
		}
		return outputFile;
	}

	public void writeAttachments(final TestCase testCase) {
		final Iterable<TestStepGroup> testStepGroups = testCase
				.getTestStepGroups();
		final String fileName = testCase.getName();
		for (TestStepGroup testStepGroup : testStepGroups) {
			writeAttachments(testStepGroup, fileName);
		}
	}

	private void writeAttachments(final TestStepGroup testStepGroup,
			final String fileName) {
		final Iterable<TestStep> testSteps = testStepGroup.getTestSteps();
		for (final TestStep testStep : testSteps) {
			writeAttachments(testStep, fileName);
		}
	}

	private void writeAttachments(final TestStep testStep, String fileName) {
		final Iterable<Attachment> attachments = testStep.getAttachments();
		int count = 0;
		for (final Attachment attachment : attachments) {
			fileName += "_" + testStep.getId() + "_" + count;
			try {
				write(attachment, fileName);
			} catch (IOException e) {
				logger.error(
						"A problem has occurred while writing the attachment "
								+ attachment.getTitle()
								+ " to the destination " + fileName, e);
			}
		}
	}

	private void write(final Attachment attachment, final String fileName)
			throws IOException {
		final java.io.File outputFile = getOutputFile(fileName,
				attachment.getFileExtension());
		setPath(attachment, outputFile);
		byte[] data = replace(attachment);
		write(outputFile, data);
	}
	
	private byte[] replace(final Attachment attachment) {
		byte[] data = attachment.getFileData();
		if (attachment instanceof StringAttachment) {
			String output = new String(data);
			output = replace(output);
			data = output.getBytes();
		}
		return data;
	}

	@SuppressWarnings("unchecked")
	private String replace(String output) {
		final Document conf = Log4TestingConfiguration.getConfig();
		final List<Node> replaceNodes = conf.selectNodes(XPATH_REPLACE);
		for (final Node replaceNode : replaceNodes) {
			final String regex = replaceNode.selectSingleNode(
					XPATH_REPLACE_REGEX).getStringValue();
			final String replacement = replaceNode.selectSingleNode(
					XPATH_REPLACE_REPLACEMENT).getStringValue();
			output = output.replaceAll(regex, replacement);
		}
		return output;
	}

	public String getPathWithoutOutputDir(final java.io.File outputFile) {
		final java.io.File outputDir = new java.io.File(
				fileWriter.getOutputdir());
		final int outputDirNameLength = outputDir.getAbsolutePath().length();
		final String absPath = outputFile.getAbsolutePath();
		final String pathWithoutOutputDir = absPath
				.substring(outputDirNameLength + 1);
		return pathWithoutOutputDir;
	}

	public String getPathToBaseDir(final java.io.File outputFile) {
		final String path = getPathWithoutOutputDir(outputFile);
		final String fullPath = FilenameUtils.getFullPath(path);
		final int pathSepCount = StringUtils.countMatches(fullPath,
				java.io.File.separator);
		final StringBuilder pathToBaseDir = new StringBuilder();
		for (int i = 0; i < pathSepCount; i++) {
			pathToBaseDir.append("..").append(java.io.File.separator);
		}
		return pathToBaseDir.toString();
	}

	public void setPath(final TestStepContainer testStepContainer) {
		try {
			setPath(testStepContainer, getOutputFile(testStepContainer));
		} catch (IOException e) {
			logger.error("An problem has occurred while setting the pathes "
					+ "of the test step container with the name "
					+ testStepContainer.getName(), e);
		}
	}

	private void setPath(final Path path, final java.io.File outputFile) {
		final String p = getPathWithoutOutputDir(outputFile);
		path.setPath(p);
		final String pToBaseDir = getPathToBaseDir(outputFile);
		path.setPathtobasedir(pToBaseDir);
	}

	private void write(final java.io.File outputFile, final byte[] data)
			throws IOException {
		final FileOutputStream fileOutputStream = new FileOutputStream(
				outputFile);
		fileOutputStream.write(data);
		fileOutputStream.flush();
		fileOutputStream.close();
	}

	private java.io.File getOutputFile(final TestStepContainer testStepContainer)
			throws IOException {
		return getOutputFile(testStepContainer.getName(),
				fileWriter.getExtension());
	}

	private java.io.File getOutputFile(final String fileName,
			final String fileExtension) throws IOException {
		String targetPath = createAndGetTargetDirPath(fileName);
		targetPath += "." + fileExtension;
		final java.io.File targetFile = new java.io.File(targetPath);
		final java.io.File parentFile = targetFile.getParentFile();
		if (!parentFile.exists()) {
			parentFile.mkdirs();
		}
		if (!targetFile.exists()) {
			targetFile.createNewFile();
		}
		return targetFile;
	}

	private String createAndGetTargetDirPath(final String fileName) {
		final java.io.File outputDir = new java.io.File(
				fileWriter.getOutputdir());
		final String packagePath = fileName.replaceAll("\\.",
				Matcher.quoteReplacement(java.io.File.separator));
		final String targetDirPath = FilenameUtils.concat(
				outputDir.getAbsolutePath(), packagePath);
		return targetDirPath;
	}

}
